package com.newbee.sustainability.web.aop;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.newbee.sustainability.common.vo.*;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 请求日志记录
 */
@Component
@Aspect
public class RequestLogAspect {
    private static final Logger LOGGER = LoggerFactory.getLogger(RequestLogAspect.class);

    @Autowired
    private ExceptionHandle exceptionHandle;


    /**
     * 切点
     * *：匹配任何数量字符；
     * ..：匹配任何数量字符的重复，如在类型模式中匹配任何数量子包；而在方法参数模式中匹配任何数量参数。
     * +：匹配指定类型的子类型；仅能作为后缀放在类型模式后边
     */
    @Pointcut("execution(* com.newbee.sustainability.web.controller..*(..))")
    public void pointCut() {

    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        long startTime = System.currentTimeMillis();
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = sra.getRequest();
        String remoteAddress = getProxyIP(request);// 获取请求IP
        String uri = request.getRequestURI();
        String methodName = pjp.getSignature().getName();
        String clazzName = pjp.getTarget().getClass().getSimpleName();

        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        // 获取参数名称，参数值
        String[] parameterNames = methodSignature.getParameterNames();
        Object[] parameterValues = pjp.getArgs();
        StringBuilder paramBuilder = new StringBuilder();
        if (parameterNames != null && parameterNames.length > 0) {
            for (int i = 0; i < parameterNames.length; i++) {
                if (parameterNames[i].equals("bindingResult")) {
                    break;
                }
                if ((parameterValues[i] instanceof HttpServletRequest) || (parameterValues[i] instanceof HttpServletResponse)) {
                    paramBuilder.
                            append("[").
                            append(parameterNames[i]).append("=").append(parameterValues[i])
                            .append("]");
                } else {
                    paramBuilder.
                            append("[").
                            append(parameterNames[i]).append("=")
                            .append(JSON.toJSONString(parameterValues[i], SerializerFeature.WriteDateUseDateFormat))
                            .append("]");
                }
            }
        }

        Object result = null;
        try {
            result = pjp.proceed();
        } catch (Exception e) {
            result = exceptionHandle.exceptionHandler(e);
        }
        String jsonString = "";
        if (result != null) {
            if (result instanceof ResponseVo) {
                jsonString = JSON.toJSONString(result, SerializerFeature.WriteDateUseDateFormat);
            } else {
                jsonString = String.valueOf(result);
            }
        }
        long endTime = System.currentTimeMillis();
        long usedTime = endTime - startTime;
        LOGGER.info("<<<<<RequestLogger>>>>> request url: 【{}】 request used time: 【{}ms】 request ip info: 【{}】 request Controller: 【{}】 " +
                        "request method: 【{}】 request param: 【{}】 response data: 【{}】", uri,
                usedTime, remoteAddress, clazzName, methodName, paramBuilder.toString(), jsonString);
        return result;
    }

    public static String getProxyIP(HttpServletRequest request) {
        String fromSource = "X-Real-IP";
        String ip = request.getHeader("X-Real-IP");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
            fromSource = "X-Forwarded-For";
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
            fromSource = "Proxy-Client-IP";
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
            fromSource = "WL-Proxy-Client-IP";
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
            fromSource = "request.getRemoteAddr";
        }
        String ipInfo = "Client IP: " + ip + ", fromSource: " + fromSource;
        return ipInfo;
    }
}
